#!perl
use strict;
use warnings;

use DBI;
use DBIx::Timeout;
use Time::HiRes qw(sleep);

# only run these tests when asked, they're long and not stable
if (not $ENV{RUN_LONG_UNSTABLE_TESTS}) {
    eval 'use Test::More skip_all => "skipping long unstable tests '
      . '- set RUN_LONG_UNSTABLE_TESTS to enable."';
} else {

    # try to make a connection to the test DB - skip all if not available
    {
        my $dbh = _dbi_connect();

        if ($@ or !$dbh) {
            eval 'use Test::More skip_all => '
              . '"Unable to run live MySQL tests - '
              . 'set DBI_DSN, DBI_USER and DBI_PASS and try again."';
            die $@ if $@;
        } else {
            eval 'use Test::More qw(no_plan)';
            die $@ if $@;
        }

        ok($dbh, 'connected to MySQL');
        $dbh->disconnect();
    }
}

# run test for $n seconds
my $n = $ENV{RUN_LONG_TESTS_FOR} || 10;
my $end_at = time + $n;

# setup a process which constantly gets a lock and holds it for random
# short periods of time between 0 and 1 seconds
my $pid = fork;
die "Failed to fork: $!" unless defined $pid;
if (not $pid) {

    # child code
    my $dbh = _dbi_connect();
    while (time <= $end_at) {
        while (1) {
            last if 
              $dbh->selectrow_array("SELECT GET_LOCK('timeout_test', 1)");
        }
        sleep(rand(1) / 2);
        $dbh->do("DO RELEASE_LOCK('timeout_test')");
    }
    exit;
}

# parent code here
while (time <= $end_at) {
    my $dbh = _dbi_connect();
    my $ok = DBIx::Timeout->call_with_timeout(
        dbh  => $dbh,
        code => sub {
            $dbh->do("DO GET_LOCK('timeout_test', 1)");
        },
        timeout => 1);

    # timeout triggered, nothing interesting
    if (not $ok) {
        ok(1, "timeout fired");
    } else {
        # timeout didn't trigger, look for evidence the connection was
        # killed anyway
        ok($dbh->{Active}, "timeout didn't fire, connection still active");
        my ($test) = eval { $dbh->selectrow_array('SELECT 1') };
        is($test, 1, "timeout didn't fire, handle works");
    }
    $dbh->disconnect();
    sleep(0.1);
}
waitpid($pid, 0);

sub _dbi_connect {
    my $test_dsn  = $ENV{'DBI_DSN'}  || 'DBI:mysql:database=test';
    my $test_user = $ENV{'DBI_USER'} || '';
    my $test_pass = $ENV{'DBI_PASS'} || '';

    return DBI->connect($test_dsn, $test_user, $test_pass, {RaiseError => 1, PrintError => 0});
}
